const events = require("events");
const {render, StateList} = require('./render');
let nativeWindow = null;

function getNativeWindow() {
    if (nativeWindow === null) {
        nativeWindow = require('electron').remote.getCurrentWindow();
    }

    return nativeWindow;
}

exports.Window = class extends events.EventEmitter {
    constructor() {
        super();

        this.id = null;
        this.data = {};

        this.title = null;
        this.x = null;
        this.y = null;
        this.width = 1000;
        this.height = 720;
        this.minWidth = 0;
        this.minHeight = 0;
        this.resizable = true;
        this.minimizable = true;
        this.closable = true;
        this.fullscreen = false;
        this.modal = false;
        this.focusable = true;
        this.alwaysOnTop = false;
        this.skipTaskbar = false;

        this.win = null;
        this.dom = null;
        this.protected = false;
        this.debug = true;

        this.focused = false;
        this.visible = false;
        this.minimized = false;

        this.displayed = true;
    }

    static Show(WindowProto, params) {
        let win = new WindowProto(params);
        win.open(params);
    }

    static Open(WindowProto, params) {
        let win = new WindowProto(params);
        win.displayed = false;
        win.open(params);
    }

    static ShowModal(WindowProto, parentId, params) {
        let win = new WindowProto(params);
        win.modal = true;
        win.parent = exports.Window.GetWindow(parentId);
        win.open(params);
    }

    static GetWindow(id) {
        if (typeof id === 'string') {
            return window.__jelly_windows__.get(id);
        } else if (id && id.ownerDocument) {
            return id.ownerDocument.defaultWindow;
        }
        return id;
    }

    static CloseAllWindow() {
        let windows = require('electron').remote.BrowserWindow.getAllWindows();
        for (let win of windows) {
            win.close();
        }
    }

    open(params) {
        let time = Date.now();
        if (this.id === null) this.id = time;
        let options = {
            id: this.id,
            openerId: this.parent && this.parent.id,
            protected: this.protected,
            debug: this.debug,
            displayed: this.displayed,
            features: {
                title: this.title,
                width: this.width,
                height: this.height,
                minWidth: this.minWidth,
                minHeight: this.minHeight,
                resizable: this.resizable,
                minimizable: this.minimizable,
                closable: this.closable,
                fullscreen: this.fullscreen,
                modal: this.modal,
                focusable: this.focusable,
                alwaysOnTop: this.alwaysOnTop,
                skipTaskbar: this.skipTaskbar
            }
        };
        if (this.parent === null) {
            const ipc = require('electron').ipcRenderer;
            ipc.send('command', 'openWindow', this.cwd(true), options, params);
        } else {
            if (typeof this.id === 'string' && window.__jelly_windows__.has(this.id)) {
                let win = window.__jelly_windows__.get(this.id);
                win.focus();
                return;
            }

            options.features.x = (typeof this.x === 'number' ? this.x : (window.screen.width - this.width) / 2);
            options.features.y = (typeof this.y === 'number' ? this.y : (window.screen.height - this.height) / 2);
            // 临时方案，用于解决
            let windowName = this.fullscreen === 'simple' ? 'simple' : JSON.stringify(options);
            let win = window.open('', windowName);
            win.document.write('<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><style>html,body{height:100%;margin:0;font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,SimSun,sans-serif;} #root{height:100%;overflow:auto;}</style></head><body></body></html>');
            win.document.close();
            win.document.defaultWindow = this;
            InitStyle(win);
            console.log('Init Time -> ' + (Date.now() - time));
            let dom = win.document.createElement('div');
            dom.id = 'root';
            this.win = win;
            this.onCreated();
            this._render(dom, win.document);
            win.document.body.appendChild(dom);
            console.log('Render Time -> ' + (Date.now() - time));
            this.onOpen(params);
            win.minimize = () => {
                win.location = 'jelly://minimize';
            };
            win.maximize = () => {
                win.location = 'jelly://maximize';
            };
            win.unmaximize = () => {
                win.location = 'jelly://unmaximize';
            };
            win.addEventListener('unload',  (e) => {
                win = null;
                this.win = null;
                window.__jelly_windows__.delete(this.id);
            }, {
                capture: true,
                once: true
            });
            if (typeof this.id === 'string') {
                window.__jelly_windows__.set(this.id, this);
            }
        }
    }

    _render(dom, doc) {
        // 处理路径
        let base = doc.createElement('base');
        base.href = 'file:///' + this.cwd(false) + '/';
        doc.head.appendChild(base);
        dom.style.width = this.width + 'px';
        dom.style.height = this.height + 'px';
        render(dom, this);
        this.dom = dom;
        this.focused = true;
        this.visible = true;
        this.win.addEventListener('focus', () => {
            this.focused = true;
            this.onFocus();
        }, true);
        this.win.addEventListener('blur', () => {
            this.focused = false;
            this.onBlur();
        }, true);
        setTimeout(() => {
            dom.style.width = '';
            dom.style.height = '';
        });
    }

    close() {
        this.win.close();
        this.win = null;
        if (typeof this.id === 'string') {
            window.__jelly_windows__.delete(this.id);
        }
    }

    show() {
        if (this.win === window) {
            getNativeWindow().show();
        } else {
            this.win.location = 'jelly://show';
        }
        this.onShow();
    }

    hide() {
        if (this.win === window) {
            getNativeWindow().hide();
        } else {
            this.win.location = 'jelly://hide';
        }
        this.onHide();
    }

    maximize() {
        this.win.maximize();
    }

    unmaximize() {
        this.win.unmaximize();
    }

    minimize() {
        this.win.minimize();
    }

    restore() {
        this.win.restore();
    }

    focus() {
        if (this.win === window) {
            getNativeWindow().focus();
        } else {
            this.win.location = 'jelly://focus';
        }
    }

    flash(flag, cb) {
        let win = getNativeWindow();
        if (flag && typeof cb === 'function') {
            win.once('focus', () => {
                cb();
            });
        }
        win.flashFrame(flag);
    }

    setTitle(title) {
        getNativeWindow().setTitle(title);
    }

    isFocused() {
        return this.focused;
    }

    isVisible() {
        return this.visible;
    }

    isMinimized() {
        return this.minimized;
    }

    setAlwaysOnTop(...args) {
        if (this.win === window) {
            getNativeWindow().setAlwaysOnTop(...args);
        } else {
            this.win.location = 'jelly://setAlwaysOnTop?' + args.join(',');
        }
    }

    setSize(...args) {
        if (this.win === window) {
            getNativeWindow().setSize(...args);
        } else {
            this.win.location = 'jelly://setSize?' + args.join(',');
        }
    }

    setBounds(bounds) {
        if (this.win === window) {
            getNativeWindow().setBounds(bounds);
        } else {
            this.win.location = 'jelly://setBounds?' + JSON.stringify(bounds);
        }
    }

    onCreated() { }

    onOpen() { }

    onClose() { }

    onShow() { }

    onHide() { }

    onMaximize() { }

    onMinimize() { }

    onRestore() { }

    onFocus() { }

    onBlur() { }

    render() { }
};

const defaultWindowMethods = Object.getOwnPropertyNames(exports.Window.prototype);

class Router {
    constructor(routerView) {
        routerView.router = this;
        this.root = routerView.shadowRoot || routerView.attachShadow({mode: 'open'});
        this.pages = [];
        this.current = -1;
        this.root.adoptedStyleSheets = routerView.ownerDocument.adoptedStyleSheets;
        this.sections = new Map();
    }

    _hideCurrentPage() {
        let dom = document.createDocumentFragment();
        dom.append.apply(dom, this.root.childNodes);
        let lastPage = this.pages[this.current];
        lastPage.dom = dom;
        lastPage.onHide();
    }

    /**
     * 路由到指定页面
     * @param {Page} page
     */
    go(page) {
        if (this.current !== -1) {
            this._hideCurrentPage();
        }

        this.pages.push(page);
        this.current = this.current + 1;

        page.router = this;
        page.dom = this.root;
        render(this.root, page);
        page.onLoad();
    }

    switch(page) {
        if (this.current !== -1) {
            // 取第一个 page 并获取 section 属性
            let lastPage = this.pages[0];
            if (lastPage.section) {
                this.sections.set(lastPage.section, this.pages.concat());
                this._hideCurrentPage();
                this.current = -1;
                this.pages = [];
            } else {
                this.clear();
            }
        }

        let pages = this.sections.get(page.section);
        if (pages) {
            this.current = pages.length - 1;
            this.pages = pages;
            let cachePage = pages[this.current];
            this.root.appendChild(cachePage.dom);
            cachePage.dom = this.root;
            this.sections.delete(page.section);
            cachePage.onShow();
        } else {
            this.go(page);
        }
    }

    back() {
        if (this.current > 0) {
            let lastPage = this.pages.pop();
            this.current = this.current - 1;
            let page = this.pages[this.current];
            let nodes = [];
            this.root.childNodes.forEach((node) => {
                nodes.push(node);
            });
            nodes.forEach((node) => {
                node.remove();
            });
            this.root.append.apply(this.root, page.dom.childNodes);
            page.dom = this.root;
            lastPage.unload();
            page.onShow();
        }
    }

    clear() {
        // 清除所有节点
        if (this.current !== -1) {
            while (this.root.firstChild) {
                this.root.removeChild(this.root.firstChild);
            }
        }

        this.pages.forEach((page) => {
            page.unload();
            page.dom = null;
            page.router = null;
        });

        this.current = -1;
        this.pages = [];
    }
}

exports.Page = class {
    constructor() {
        this.dom = null;
        this.router = null;
        this.data = {};
        this.section = null;
    }

    load(pageView) {
        if (pageView.router) {
            pageView.router.go(this);
        } else {
            let router = new Router(pageView);
            router.go(this);
        }
    }

    unload() {
        this.onUnload();
        if (this.data) this.data.__ob__.destroy();
    }

    onLoad() { }

    onUnload() { }

    onShow() { }

    onHide() { }

    render() { }
};

const ComponentState = {
    NONE: 0,        // 无状态
    ACTIVATE: 1,      // 活动
    DEACTIVATE: 2     // 停用
};

exports.Component = class extends HTMLElement {
    constructor(props, open = false) {
        super();

        // 组件状态
        this.comstate = ComponentState.NONE;

        this.data = {};
        this.props = props || {};
        this.unloadTimer = null;

        if (arguments.length <= 1 && props === undefined) this.initProps();

        this.root = open ? this : this.attachShadow({mode: 'closed'});
    }

    load(parentDom = document.body) {
        if (this.comstate === ComponentState.NONE) {
            parentDom.appendChild(this);
        }
    }

    unload() {
        if (this.comstate !== ComponentState.NONE) {
            this.parentNode.removeChild(this);
        }
    }

    async(cb) {
        return (...args) => {
            if (this.comstate !== ComponentState.NONE) cb(...args);
        };
    }

    deactivate() {
        if (this.comstate === ComponentState.ACTIVATE) {
            this.comstate = ComponentState.DEACTIVATE;
            let fragment = document.createDocumentFragment();
            fragment.appendChild(this);
            //this.parentNode.removeChild(this);
        }
    }

    reactivate(parentDom = document.body) {
        if (this.comstate === ComponentState.DEACTIVATE) {
            this.comstate = ComponentState.ACTIVATE;
            let d = Date.now();
            parentDom.appendChild(this);
            console.log('Component Reactivate 耗时: ' + (Date.now() - d));
            this.onReactivated();
        }
    }

    initProps() {
        let attributes = this.attributes;
        for (let attr of attributes) {
            let name = attr.name;
            if (name !== 'style' && name.indexOf('@') === -1) {
                let camelName = name.replace(/-(\w)/g, function(all, letter){
                    return letter.toUpperCase();
                });
                this.props[camelName] = attr.value;
            }
        }
    }

    render() { }

    onCreated() { }

    onLoad() { }

    onUnload() { }

    onUnloadBefore() { }

    onDeactivated() { }

    onReactivated() { }

    connectedCallback() {
        clearTimeout(this.unloadTimer);

        if (this.comstate === ComponentState.NONE) {
            this.root.adoptedStyleSheets = this.ownerDocument.adoptedStyleSheets;

            this.comstate = ComponentState.ACTIVATE;
            this.onCreated();
            render(this.root, this);
            this.onLoad();
        } else if (this.comstate === ComponentState.DEACTIVATE) {
            this.comstate = ComponentState.ACTIVATE;
            this.onReactivated();
        }
    }

    disconnectedCallback() {
        let root = this.getRootNode();
        while (root instanceof ShadowRoot) {
            root = root.host.getRootNode();
        }
        if (root instanceof DocumentFragment) {
            this.onDeactivated();
        } else {
            this.onUnloadBefore();
            this.unloadTimer = setTimeout(() => {
                this.comstate = ComponentState.NONE;
                this.onUnload();
                if (this.data) this.data.__ob__.destroy();
            });
        }
    }

    static Register(win = window) {
        let name = ((re, str) => str.replace(re, '$1-$2').replace(re, '$1-$2').toLowerCase())(/([^-])([A-Z])/g, this.name);
        if (win.customElements.get(name) !== undefined) return;
        win.customElements.define(name, this);
    }

    static toString() {
        let name = ((re, str) => str.replace(re, '$1-$2').replace(re, '$1-$2').toLowerCase())(/([^-])([A-Z])/g, this.name);
        return '<' + name + '></' + name + '>';
    }
};

exports.openPath = (path) => {
    const ipc = require('electron').ipcRenderer;
    ipc.send('command', 'openPath', path);
};
exports.openExternal = (url, options) => {
    const ipc = require('electron').ipcRenderer;
    ipc.send('command', 'openExternal', url, options);
};
exports.showItemInFolder = (fullPath) => {
    const ipc = require('electron').ipcRenderer;
    ipc.send('command', 'showItemInFolder', fullPath);
};

exports.StateList = StateList;
